package com.benyanyi.loglib

import android.text.TextUtils
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import java.lang.StringBuilder

/**
 * @author myLove
 * @time 2017/10/31 10:39
 * @e-mail ben@yanyi.red
 * @overview
 */
object Jlog {
    @JvmField
    internal val LINE_SEPARATOR = System.getProperty("line.separator")
    private const val DEFAULT_MESSAGE = "********"
    private const val PARAM = "Param"
    private const val NULL = "null"
    internal const val JSON_INDENT = 4

    @JvmField
    internal var mIsGlobalTagEmpty = true

    @JvmField
    internal var IS_INIT = true

    @JvmField
    internal var mConfig = LogConfig()

    fun init(): LogConfig {
        return mConfig
    }

    fun init(config: LogConfig) {
        mConfig = config
    }

    fun v() {
        printLog(LogType.V, null, DEFAULT_MESSAGE)
    }

    fun v(msg: Any?) {
        printLog(LogType.V, null, msg!!)
    }

    fun v(length: Int, msg: Any?) {
        printLog(LogType.V, null, msg!!)
    }

    fun v(tag: Any?, vararg objects: Any) {
        printLog(LogType.V, tag, *objects)
    }

    fun d() {
        printLog(LogType.D, null, DEFAULT_MESSAGE)
    }

    fun d(msg: Any?) {
        printLog(LogType.D, null, msg!!)
    }

    fun d(tag: Any?, vararg objects: Any?) {
        printLog(LogType.D, tag, *objects)
    }

    fun i() {
        printLog(LogType.I, null, DEFAULT_MESSAGE)
    }

    fun i(msg: Any?) {
        printLog(LogType.I, null, msg!!)
    }

    fun i(tag: Any?, vararg objects: Any?) {
        printLog(LogType.I, tag, *objects)
    }

    fun w() {
        printLog(LogType.W, null, DEFAULT_MESSAGE)
    }

    fun w(msg: Any?) {
        printLog(LogType.W, null, msg!!)
    }

    fun w(tag: Any?, vararg objects: Any?) {
        printLog(LogType.W, tag, *objects)
    }

    fun e() {
        printLog(LogType.E, null, DEFAULT_MESSAGE)
    }

    fun e(msg: Any?) {
        printLog(LogType.E, null, msg!!)
    }

    fun e(tag: Any?, vararg objects: Any?) {
        printLog(LogType.E, tag, *objects)
    }

    fun a() {
        printLog(LogType.A, null, DEFAULT_MESSAGE)
    }

    fun a(msg: Any?) {
        printLog(LogType.A, null, msg!!)
    }

    fun a(tag: Any?, vararg objects: Any?) {
        printLog(LogType.A, tag, *objects)
    }

    fun json(jsonFormat: Any?) {
        printLog(LogType.JSON, null, jsonFormat!!)
    }

    fun json(tag: Any?, jsonFormat: String?) {
        printLog(LogType.JSON, tag, jsonFormat!!)
    }

    fun xml(xml: Any?) {
        printLog(LogType.XML, null, xml!!)
    }

    fun xml(tag: Any?, xml: String?) {
        printLog(LogType.XML, tag, xml!!)
    }

    fun file(targetDirectory: File, msg: Any) {
        printFile(null, targetDirectory, null, msg)
    }

    fun file(tag: Any?, targetDirectory: File, msg: Any) {
        printFile(tag, targetDirectory, null, msg)
    }

    fun file(tag: Any?, targetDirectory: File, fileName: String?, msg: Any) {
        printFile(tag, targetDirectory, fileName, msg)
    }

    fun debug() {
        printDebug(null, DEFAULT_MESSAGE)
    }

    fun debug(msg: Any?) {
        printDebug(null, msg!!)
    }

    fun debug(tag: Any?, vararg objects: Any?) {
        printDebug(tag, *objects)
    }

    fun trace() {
        printStackTrace()
    }

    fun errorWriteFile(throwable: Throwable) {
        if (mConfig.isWrite) {
            BaseLog.writeError(mConfig.tag ?: "", throwable)
        }
    }

    fun errorWriteFile(tag: String, throwable: Throwable) {
        if (mConfig.isWrite) {
            BaseLog.writeError(tag, throwable)
        }
    }

    private fun printStackTrace() {
        if (!IS_INIT && !mConfig.isWrite) {
            return
        }
        if (!mConfig.isShowLog && isApkInDebug && !mConfig.isWrite) {
            return
        }
        val tr = Throwable()
        val sw = StringWriter()
        val pw = PrintWriter(sw)
        tr.printStackTrace(pw)
        pw.flush()
        val message = sw.toString()
        val traceString = message.split("\\n\\t".toRegex()).toTypedArray()
        val sb = StringBuilder()
        sb.append("\n")
        for (trace in traceString) {
            if (trace.contains("at Jlog")) {
                continue
            }
            sb.append(trace).append("\n")
        }
        val contents = wrapperContent("null", sb.toString()) ?: return
        val tag = contents[0]
        val msg = contents[1]
        val headString = contents[2]
        BaseLog.printDefault(LogType.D, tag, headString + msg)
    }

    private fun printLog(type: LogType, tagStr: Any?, vararg objects: Any?) {
        if (!IS_INIT && !mConfig.isWrite) {
            return
        }
        if (!mConfig.isShowLog && isApkInDebug && !mConfig.isWrite) {
            return
        }
        val contents = wrapperContent(tagStr, *objects) ?: return
        val tag = contents[0]
        val msg = contents[1]
        val headString = contents[2]
        when (type) {
            LogType.V, LogType.D, LogType.I, LogType.W, LogType.E, LogType.A -> BaseLog.printDefault(
                type,
                tag,
                headString + msg
            )
            LogType.JSON -> JsonLog.printJson(tag, msg, headString)
            LogType.XML -> XmlLog.printXml(tag, msg, headString)
        }
    }

    private fun printDebug(tagStr: Any?, vararg objects: Any?) {
        if (!IS_INIT || !mConfig.isWrite) {
            return
        }
        val contents = wrapperContent(tagStr, *objects) ?: return
        val tag = contents[0]
        val msg = contents[1]
        val headString = contents[2]
        BaseLog.printDefault(LogType.D, tag, headString + msg)
    }

    private fun printFile(tagStr: Any?, targetDirectory: File, fileName: String?, objectMsg: Any) {
        if (!IS_INIT && !mConfig.isWrite) {
            return
        }
        if (!mConfig.isShowLog && isApkInDebug && !mConfig.isWrite) {
            return
        }
        val contents = wrapperContent(tagStr, objectMsg) ?: return
        val tag = contents[0]
        val msg = contents[1]
        val headString = contents[2]
        FileLog.printFile(tag, targetDirectory, fileName, headString, msg)
    }

    private fun wrapperContent(tagStr: Any?, vararg objects: Any?): Array<String>? {
        val stackTrace = Thread.currentThread().stackTrace
        var fileName = ""
        var targetElement: StackTraceElement? = null
        for (element in stackTrace) {
            val name = element.fileName
            val packageName = element.javaClass.getPackage()!!.name
            if ("VMStack.java" != name && "Thread.java" != name && "Jlog.kt" != name
                && "KtUtils.kt" != name
            ) {
                fileName = name
                targetElement = element
                break
            }
        }
        if (targetElement == null) {
            return null
        }
        val str = "$"
        if (fileName.contains(str)) {
            fileName = fileName.split("\\$".toRegex()).toTypedArray()[0]
        }
        val methodName = targetElement.methodName
        var lineNumber = targetElement.lineNumber
        if (lineNumber < 0) {
            lineNumber = 0
        }
        var tag = (tagStr ?: fileName).toString()
        if (mIsGlobalTagEmpty && TextUtils.isEmpty(tag)) {
            tag = mConfig.tagDefault
        } else if (!mIsGlobalTagEmpty) {
            tag = mConfig.tagDefault
        }
        val msg = getObjectsString(*objects)
        val headString = "[ ($fileName:$lineNumber)#$methodName ] "
        return arrayOf(tag, msg, headString)
    }

    private fun getObjectsString(vararg objects: Any?): String {
        return if (objects.size > 1) {
            val stringBuilder = StringBuilder()
            stringBuilder.append("\n")
            for (i in objects.indices) {
                val `object` = objects[i]
                if (`object` == null) {
                    stringBuilder.append(PARAM).append("[").append(i).append("]").append(" = ")
                        .append(
                            NULL
                        ).append("\n")
                } else {
                    stringBuilder.append(PARAM).append("[").append(i).append("]").append(" = ")
                        .append(`object`).append("\n")
                }
            }
            stringBuilder.toString()
        } else {
            val `object` = objects[0]
            `object`?.toString() ?: NULL
        }
    }

    //        try {
//            @SuppressLint("PrivateApi")
//            Class<?> activityThread = Class.forName("android.app.ActivityThread");
//            Object thread = activityThread.getMethod("currentActivityThread").invoke(null);
//            Object app = activityThread.getMethod("getApplication").invoke(thread);
//            Application application;
//            if (app == null) {
//                application = null;
//            } else {
//                application = (Application) app;
//            }
//            ApplicationInfo info = application.getApplicationInfo();
//            return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0;
//        } catch (Exception e) {
//            return false;
//        }
    private val isApkInDebug: Boolean
        get() = BuildConfig.DEBUG
    //        try {
//            @SuppressLint("PrivateApi")
//            Class<?> activityThread = Class.forName("android.app.ActivityThread");
//            Object thread = activityThread.getMethod("currentActivityThread").invoke(null);
//            Object app = activityThread.getMethod("getApplication").invoke(thread);
//            Application application;
//            if (app == null) {
//                application = null;
//            } else {
//                application = (Application) app;
//            }
//            ApplicationInfo info = application.getApplicationInfo();
//            return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0;
//        } catch (Exception e) {
//            return false;
//        }


    fun aaa(vararg str: String) {}

}